﻿
open System
open Microsoft.FSharp.Text.Lexing
open System.IO
open System.Windows.Forms
open Parser
open Lexer
open SymbolTableTypes
open SymbolTableGenerator
open EmitGenerator

open PrintUtils
Printf.printfn "TinyJavaCompiler by Abbadi Mohamed && Steffinlongo Enrico"



let rec readAndProcess source dest opt windowed =
  let input = 
    if windowed then    
      let dlg = new OpenFileDialog()
      do dlg.Filter <- "Tiny Java sources (*.cs;*.tj)|*.cs;*.tj|All files (*.*)|*.*"
      if dlg.ShowDialog() = DialogResult.OK then
        dlg.FileName
      else
        ""
    else
      source

  let output = 
    if windowed && input <> "" then    
      let dlg = new SaveFileDialog()
      do dlg.Filter <- "Assembies (*.s;*.asm)|*.s;*.asm"
      if dlg.ShowDialog() = DialogResult.OK then
        dlg.FileName
      else
        ""
    else
      dest
  
  if input <> "" && output <> "" then
    if File.Exists(input) |> not then
      Console.WriteLine (sprintf "file %s doesn't exists" input)
      Console.WriteLine("press any key to continue...")
      Console.ReadKey() |> ignore
    else
      let inputChannel = new StreamReader(input)
      let lexbuf = LexBuffer<char>.FromTextReader inputChannel
  //    let fin = File.ReadAllText(dlg.FileName)
  //    //printfn "Lexing [%s]" fin
  //    let lexbuff = LexBuffer<char>.FromString(fin)
      //do find_tokens lexbuff
      try 
        let p = Parser.start Lexer.tokenize lexbuf
        do ReturnCheck.check_ret p
        let tables = type_check_Program p
        let emit = generate_emit p tables opt

        do AssemblyCodeGenerator.generate_main_code (Path.GetFileName input) tables emit output

        if windowed then
          do AstPrintForm.showAst p input
      with e ->
        match e with
          |Parser.ParseErrorContext ctx ->
            let message = ctx :?> Parsing.ParseErrorContext<Parser.token> //ctx :?> Parsing.ParseErrorContext<_>
            match message.CurrentToken with
              |None -> ()
              |Some tk ->
                Console.WriteLine("error parsing " + (token_to_string tk))
          | _ when e.Message = "unrecognized input" ->
            Console.WriteLine(sprintf "ERROR\nunrecognized input at line %d character %d " (lexbuf.EndPos.pos_lnum + 1) (lexbuf.EndPos.pos_cnum - lexbuf.EndPos.pos_bol))
          | _ when e.Message.StartsWith("Name already exists in: ") ->          
            Console.WriteLine(e.Message)
          | e ->
            Console.WriteLine(e.Message)
            if windowed then
              Console.WriteLine("show AST? (s/n)")
              if Console.ReadLine() = "s" then
                let inputChannel = new StreamReader(input)
                let lexbuf = LexBuffer<char>.FromTextReader inputChannel
                do AstPrintForm.showAst (Parser.start Lexer.tokenize lexbuf) input
              
        Console.WriteLine("press any key to continue...")
        Console.ReadKey() |> ignore
      

  else
    Console.WriteLine("Aborted")

let rec private manage_args (args : string list) : Choice<(string * string * bool), Unit, Unit>=
  match args with
  | opt::args when opt = "-O" -> 
    let (Choice1Of3 (source, out, opt)) = manage_args args
    Choice1Of3 (source, out, true)
  | out::name::args when out = "-o" ->
    let (Choice1Of3 (source, out, opt)) = manage_args args
    Choice1Of3 (source, name, opt)
  | x::[] when x ="--help" -> 
    Choice2Of3(())
  | source::[] ->
    Choice1Of3 (source, "out.s", false)
  | [] -> 
    Choice3Of3()
  | o::xs ->
    Console.WriteLine (sprintf "tjc: unrecognized option %s" o)
    manage_args xs 

[<STAThreadAttribute>]
[<EntryPoint>]
let main args =
  if args |> Seq.exists ((=) "-w") then
    do readAndProcess "" "" false true
  else
    match manage_args (args |> Seq.toList) with
    | Choice1Of3 (source, out, opt) ->
      do readAndProcess source out opt false
    | Choice2Of3 _ ->
      Console.WriteLine "help"
    | Choice3Of3 _ ->
      Console.WriteLine "no input files"   
  
  0
  // Return 0. This indicates success.
  



